Skip to content

Conversation

@maxclaus
Copy link

Description

MCP tool calls now properly continue distributed traces from parent services instead of creating new transactions with new trace IDs.

Problem: When get_start_span_function() executes during MCP tool handler wrapping, the HTTP transaction isn't accessible due to async context isolation. The function only checks the current scope, which often contains unrelated spans with different trace IDs.

Solution: Enhanced get_start_span_function() to check for sentry-trace headers in MCP request context. Added _create_transaction_from_mcp_headers() that extracts headers and uses continue_trace() to create transactions inheriting trace_id and parent_span_id.

Benefits:

  • MCP tools join distributed traces correctly
  • Transactions still appear in MCP Insights dashboard
  • Fully backward compatible
  • No new dependencies

Issues

N/A (External contribution)

MCP tool calls now properly continue distributed traces from parent services
instead of creating new transactions with new trace IDs.

The issue occurred because when get_start_span_function() executes during MCP
tool handler wrapping, the HTTP transaction isn't accessible due to async
context isolation. The function only checked the current scope, which often
contained unrelated spans with different trace IDs.

This fix adds checking for sentry-trace headers in the MCP request context
and uses continue_trace() to create transactions that inherit the trace_id
and parent_span_id from incoming requests.

Benefits:
- MCP tools join distributed traces correctly
- Transactions still appear in MCP Insights dashboard
- Fully backward compatible
- No new dependencies
@maxclaus maxclaus requested a review from a team as a code owner December 31, 2025 03:09
)

# Start transaction on scope so it becomes active
return sentry_sdk.start_transaction(transaction=transaction)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kwargs ignored in MCP headers success path

The **kwargs parameter is passed to start_transaction() in the fallback path (line 181) but completely ignored when the MCP headers path succeeds (line 176). This causes inconsistent behavior where additional keyword arguments are applied only when the primary code path fails, leading to silent differences in transaction configuration depending on whether MCP headers are present.

Additional Locations (1)

Fix in Cursor Fix in Web

sentry_trace = request.headers.get("sentry-trace")
if sentry_trace:
return _create_transaction_from_mcp_headers
except (ImportError, LookupError):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AttributeError uncaught when headers attribute is None

In get_start_span_function(), the code uses hasattr(request, "headers") to check if the attribute exists, then immediately calls request.headers.get("sentry-trace"). If headers exists but is None, this raises an AttributeError that isn't caught by the except (ImportError, LookupError) handler. The similar code in _create_transaction_from_mcp_headers() catches all Exception, but this function would fail and propagate the error.

Fix in Cursor Fix in Web

@maxclaus
Copy link
Author

I am closing this PR and will open an issue next to discuss about it first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant